home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * tracecntl.c,v 1.1.1.1 1994/04/04 04:29:49 amiga Exp
- *
- * tracecntl.c,v
- * Revision 1.1.1.1 1994/04/04 04:29:49 amiga
- * Initial CVS check in.
- *
- * Revision 1.6 1993/11/05 22:03:32 mwild
- * treat vfork2 same as vfork
- *
- * Revision 1.5 1992/08/09 21:01:09 amiga
- * import sysbase
- *
- * Revision 1.4 1992/07/04 19:23:16 mwild
- * double the number of passed parameters. Probably still not enough for weird
- * cases, but I can't do much about that...
- *
- * Revision 1.3 1992/05/22 01:51:07 mwild
- * all common double returning functions are _JMP or they won't work
- *
- * Revision 1.2 1992/05/18 12:24:20 mwild
- * new way of getting at the result of a function. Do the call from
- * inside the handler and tell the library call hook to not call the
- * function again (TRACE_ACTION_RTS). Removed trace_exit() handler.
- *
- * Revision 1.1 1992/05/14 19:55:40 mwild
- * Initial revision
- *
- */
-
- #define _KERNEL
- #include "ixemul.h"
- #include "kprintf.h"
-
- #include <sys/tracecntl.h>
-
- #ifdef TRACE_LIBRARY
-
- static struct List packets = {
- (struct Node *) &packets.lh_Tail, 0, (struct Node *) &packets.lh_Head,
- };
-
- static struct SignalSemaphore psem;
- static sem_initialized = 0;
-
- /* for each function traced, the trace_entry() function decides whether
- the trace_exit() function is invoked or not.
- If trace_entry() returns false, trace_exit() is not invoked.
-
- NOTE: having setjmp, vfork and the like invoke trace_exit will *NOT*
- work and will cause crashes!!
-
- Since I consider placing break points just to get the return value
- a bit overkill, I'll take a less optimal solution: I copy 8 args, this
- will do for 99% of all cases, and some nasty printf() style call will
- probably fail if tracing is enabled, so what ;-)) */
-
- int
- trace_entry (int scall, int (*func)(int, ...), void *ret_addr,
- int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8,
- int a9, int aa, int ab, int ac, int ad, int ae, int af)
- {
- struct trace_packet *tp, *ntp;
- struct Task *me = FindTask (0);
- int te_action, handler_active;
- int omask;
-
- /* for safety, don't do anything if running in Forbid() or Disable() */
- if (SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0)
- return TRACE_ACTION_JMP;
-
- /* have to do this here, or the handler may get into a deadlock
- situation when trying to obtain the semaphore */
- if (u.u_trace_flags)
- return TRACE_ACTION_JMP;
-
- /* get default action value */
- switch (scall)
- {
- case SYS_abort:
- case SYS_exit:
- case SYS_longjmp:
- case SYS_setjmp: /* can return twice ! */
- case SYS_siglongjmp:
- case SYS_sigreturn:
- case SYS_sigsetjmp: /* "" */
- case SYS__exit:
- case SYS__longjmp:
- case SYS__setjmp: /* "" */
- case SYS_vfork: /* "" */
- case SYS_vfork2: /* "" */
- case SYS_vfork_resume: /* does longjmp-y thing... */
- case SYS_execve:
- case SYS_ix_geta4: /* special, result is in A4, not D0 ;-)) */
- case SYS_ix_check_cpu: /* may not return (but isn't used currently ;-)) */
- case SYS_ix_startup: /* those two call longjmp thru _exit */
- case SYS_ix_exec_entry:
- case SYS_fork:
- case SYS_floor: /* have all functions returning more than */
- case SYS_ceil: /* 4 bytes be called JMP'y */
- case SYS_atof:
- case SYS_frexp:
- case SYS_modf:
- case SYS_ldexp:
- case SYS_atan ... SYS_fabs: /* all the trigo stuff from *transbase.. */
- case SYS_strtod:
- te_action = TRACE_ACTION_JMP;
- break;
-
- default:
- te_action = TRACE_ACTION_JSR;
- break;
- }
-
- /* don't use syscall() here.. */
- omask = sigsetmask (~0);
- if (! sem_initialized)
- {
- sem_initialized = 1;
- InitSemaphore (& psem);
- }
- ObtainSemaphore (& psem);
- handler_active = 0;
- for (tp = (struct trace_packet *) packets.lh_Head;
- (ntp = (struct trace_packet *) tp->tp_message.mn_Node.ln_Succ);
- tp = ntp)
- {
- if ((!tp->tp_pid || tp->tp_pid == (pid_t) me) &&
- (!tp->tp_syscall || tp->tp_syscall == scall))
- {
- Remove ((struct Node *) tp);
-
- tp->tp_is_entry = 1;
- tp->tp_argv = &scall;
- tp->tp_errno = u.u_errno;
- /* provide the default for the handler to (possibly) override */
- tp->tp_action = te_action;
- /* wanted to use u.u_sync_mp here, but this leads to some
- deadlock situations when the port is used for packets.. */
- tp->tp_message.mn_ReplyPort = (struct MsgPort *) me;
- SetSignal (0, SIGBREAKF_CTRL_E);
- PutMsg (tp->tp_tracer_port, (struct Message *) tp);
- Wait (SIGBREAKF_CTRL_E);
- /* the last handler wins ;-)) */
- te_action = tp->tp_action;
- handler_active = 1;
-
- /* should be safe.. */
- AddHead (&packets, (struct Node *) tp);
- }
- }
- ReleaseSemaphore (& psem);
- sigsetmask (omask);
-
- if (! handler_active)
- te_action = TRACE_ACTION_JMP;
-
- switch (te_action)
- {
- case TRACE_ACTION_ABORT:
- abort();
-
- default:
- case TRACE_ACTION_JMP:
- return TRACE_ACTION_JMP;
-
- case TRACE_ACTION_JSR:
- {
- int result, error;
-
- /* we now know that there is at least one trace handler
- interested in this result, so do the extra overhead of
- calling with (excess) argument copying */
- result = func (a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af);
- error = errno;
-
- /* replace the function address with the result */
- *(int *)&func = result;
-
- /* and repeat the process of calling the trace handler(s) */
- omask = sigsetmask (~0);
- ObtainSemaphore (& psem);
- for (tp = (struct trace_packet *) packets.lh_Head;
- (ntp = (struct trace_packet *) tp->tp_message.mn_Node.ln_Succ);
- tp = ntp)
- {
- if ((!tp->tp_pid || tp->tp_pid == (pid_t) me) &&
- (!tp->tp_syscall || tp->tp_syscall == scall))
- {
- Remove ((struct Node *) tp);
-
- tp->tp_is_entry = 0;
- tp->tp_argv = &scall;
- errno = error;
- KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
- tp->tp_errno = u.u_errno;
- tp->tp_message.mn_ReplyPort = (struct MsgPort *) me;
- SetSignal (0, SIGBREAKF_CTRL_E);
- PutMsg (tp->tp_tracer_port, (struct Message *) tp);
- Wait (SIGBREAKF_CTRL_E);
-
- error = errno;
- /* should be safe.. */
- AddHead (&packets, (struct Node *) tp);
- }
- }
- ReleaseSemaphore (& psem);
- sigsetmask (omask);
- errno = error;
- KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
- }
- /* fall into */
-
- case TRACE_ACTION_RTS:
- return TRACE_ACTION_RTS;
- }
- }
-
- #endif /* TRACE_LIBRARY */
-
-
- int
- tracecntl (enum trace_cmd cmd, struct trace_packet *tp)
- {
- #ifndef TRACE_LIBRARY
- errno = ENOSYS;
- KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
- return -1;
- #else
- switch (cmd)
- {
- case TRACE_INSTALL_HANDLER:
- ix_lock_base ();
- tp->tp_message.mn_Node.ln_Type = NT_MESSAGE;
- tp->tp_message.mn_Length = sizeof (struct trace_packet);
- ObtainSemaphore (& psem);
- AddTail (&packets, (struct Node *) tp);
- ReleaseSemaphore (& psem);
- ix_unlock_base ();
- u.u_trace_flags = 1;
- return 0;
-
- case TRACE_REMOVE_HANDLER:
- ix_lock_base ();
- ObtainSemaphore (& psem);
- Remove ((struct Node *) tp);
- ReleaseSemaphore (& psem);
- ix_unlock_base ();
- u.u_trace_flags = 0;
- return 0;
-
- default:
- errno = EINVAL;
- KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
- return -1;
- }
- #endif
- }
-